#include "ParallelMatrixMultiply.h"
#include "Matrix.h"
#include <climits>
#include <ctime>
#include <cfloat>
#include <fstream>
#include <iostream>
#include <cstdlib>
#include <omp.h>
#include <math.h>

#include "method1.h"
#include "method2.h"
#include "method3.h"
#include "dgemm.h"

using namespace std;

typedef void (MatrixMultiply::*MatrixMultiplyFunc)(const int threads);

double measure(const int iter, MatrixMultiply& mm, MatrixMultiplyFunc func, const int threads = 1, const int maxRep = 2);
const int iterFromModel(const double time);

int main(int argc, const char* argv[])
{
	int maxCount = 2000;
	int step = 1;

	const int NO_OF_METHODS = 4;
	MatrixMultiplyFunc func[NO_OF_METHODS];
	func[0] = &MatrixMultiply::cblas_myDGEMM;
	func[1] = &MatrixMultiply::method1;
	func[2] = &MatrixMultiply::method2;
	func[3] = &MatrixMultiply::method3;

	const int NO_OF_THREAD_TESTS = 4;
	int threads[NO_OF_THREAD_TESTS];
	threads[0] = 1;
	threads[1] = 2;
	threads[2] = 3;
	threads[3] = 4;

	const char *fileName = "zeitmessung.txt";
	if(argc >= 2)
	{
		fileName = argv[1];
	}
	cout << "writing " << fileName << endl;
	cout.precision(5);
	fstream myfile;
	myfile.open(fileName, ios::out | ios::trunc);
	//FileHead
	myfile << "Problemgroesse dgemm Method1_1thread Method1_2thread Method1_3thread Method1_4thread Method2_1thread Method2_2thread Method2_3thread Method2_4thread Method3_1thread Method3_2thread Method3_3thread Method3_4thread";

	int iter[NO_OF_METHODS];
	double time;
	for(int n=96; n <= maxCount; n+=step)
	{
		int ld = (n%2) ? n : n+1;
		myfile << endl << n << " ";
		cout << endl << n << "(ld:" << ld << ")" << " ";
		iter[0] = iterFromModel(dgemm_model(n));
		iter[1] = iterFromModel(method1_model(n));
		iter[2] = iterFromModel(method2_model(n));
		iter[3] = iterFromModel(method3_model(n));

		for(int i=0; i<sizeof(iter)/sizeof(iter[0]); ++i)
		{
			for(int t=0; t<(i==0?1:sizeof(threads)/sizeof(threads[0])); ++t)
			{
				Matrix matA(n, ld);
				Matrix matB(n, ld);
				Matrix matC(n, ld, true);
				MatrixMultiply mm(&matA, &matB, &matC);
				time = measure(iter[i], mm, func[i], threads[t]);
				myfile << time << " ";
				cout << fixed << time << "(i:" << iter[i] << ") ";
				//cout << endl << matC;
			}
		}
	}
	myfile.close();
	cout << endl << "Done with the Measuring" << endl;//, press Enter to Exit" << endl;
	//cin.get();
	return 0;
}

double measure(const int iter, MatrixMultiply& mm, MatrixMultiplyFunc func, const int threads, const int maxRep)
{
	double time = (double)FLT_MAX;
	double diter = (double)iter; // /threads
	for(int rep = 0; rep < maxRep; rep++)
	{
		double start = omp_get_wtime();
		for(int its=0; its < iter; its++)
		{
			(mm.*func)(threads);
		}
		double stop = omp_get_wtime();
		double t = (stop-start)/iter;
		time = min(time, t);
	}
	return time;
}

const int iterFromModel(const double time)
{
	const double minTime = 0.02;
	return min(1000, max(1, (int)ceil(minTime/time)));
}
